﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TjZheng.Core.Mvc;
using TjZheng.Framework;
using TjZheng.Logging;
using TjZheng.Logging.Service;

namespace TjZheng.JiaLi.Process
{
    public abstract class JiaLiProcessHandler : ProcessHandler
    {
        /// <summary>
        /// 打印消息
        /// </summary>
        public event Action<object> PrintMessage;
        /// <summary>
        /// 打印消息
        /// </summary>
        /// <param name="message"></param>
        public void ShowMessage(object message)
        {
            if (this.PrintMessage != null)
            {
                this.PrintMessage(message);
            }
        }
        /// <summary>
        /// 批量抓取页码更新事件
        /// </summary>
        public event Action<JiaLiContext> PgaeIndexChange;
        /// <summary>
        /// 日志组件
        /// </summary>
        public ILogService LogService { get; private set; }
        /// <summary>
        /// 下载队列
        /// </summary>
        public ConcurrentQueue<ImageInfo> ConcurrentQueue { get; private set; }
        /// <summary>
        /// 构造函数
        /// </summary>
        public JiaLiProcessHandler()
        {
            this.LogService = LogConfiguration.LogServiceProvider.CreateLogService();
            this.ConcurrentQueue = new ConcurrentQueue<ImageInfo>();
        }
        /// <summary>
        /// 从来源地址抓取图片下载地址
        /// </summary>
        /// <param name="objJiaLiContext"></param>
        /// <param name="sourceUrl"></param>
        /// <returns></returns>
        protected abstract SourceInfo GetSourceInfo(JiaLiContext objJiaLiContext, string sourceUrl);

        /// <summary>
        /// 从目标地址抓取地址列表
        /// </summary>
        /// <param name="objJiaLiContext"></param>
        /// <param name="targetUrl"></param>
        /// <returns></returns>
        protected abstract TargetInfo GetTargetInfo(JiaLiContext objJiaLiContext, string targetUrl);
        /// <summary>
        /// 从抓取的地址上保存图片
        /// </summary>
        /// <param name="objJiaLiContext"></param>
        /// <param name="catchUrl"></param>
        protected abstract void SaveImagesFromCatch(JiaLiContext objJiaLiContext, string catchUrl);
        /// <summary>
        /// 保存图片
        /// </summary>
        /// <param name="objJiaLiContext"></param>
        /// <param name="sourceUrl"></param>
        protected virtual void SaveImagesFromSource(JiaLiContext objJiaLiContext, SourceInfo objSourceInfo)
        {
            if (objJiaLiContext.IsStop)
            {
                return;
            }
            if (objSourceInfo == null || objSourceInfo.FromSource == null || objSourceInfo.ImageInfos.IsNull())
            {
                return;
            }
            ImageInfo objDequeueImageInfo = null;
            foreach (ImageInfo objImageInfo in objSourceInfo.ImageInfos)
            {
                if (objJiaLiContext.IsStop)
                {
                    return;
                }
                this.ConcurrentQueue.Enqueue(objImageInfo);
                //避免下载任务过多时，带宽超负荷，下载超时
                while (this.ConcurrentQueue.Count > 25)
                {
                    Thread.Sleep(5000);
                }
                ThreadPool.QueueUserWorkItem((state) =>
                {
                    while (this.ConcurrentQueue.TryDequeue(out objDequeueImageInfo))
                    {
                        this.SaveImage(objJiaLiContext, objDequeueImageInfo);
                    }
                });
            }
        }

        protected virtual void SaveImage(JiaLiContext objJiaLiContext, ImageInfo objImageInfo)
        {
            string writePath = string.Format(@"{0}\{1}\{2}", objJiaLiContext.Directory, objImageInfo.SourceInfo.FromSource.PageTitle, Path.GetFileName(objImageInfo.ImageUrl)).Replace("\r", "").Replace("\n", "").Replace("\t", "");
            if (writePath.IsFileExist())
            {
                this.ShowMessage("请求图片地址：【" + objImageInfo.ImageUrl + "】\r\n文件已存在，文件路径：【" + writePath + "】");
                Thread.Sleep(200);
                return;
            }
            writePath.CreateDirectory();
            this.RepeatExecute(objJiaLiContext,
            () =>
            {
                this.ShowMessage("请求图片地址：【" + objImageInfo.ImageUrl + "】");
                using (var objResponse = objImageInfo.ImageUrl.CreateHttpRequest(null, null, null, null, null, 100000).GetResponse())
                {
                    using (var objStream = objResponse.GetResponseStream())
                    {
                        Image objImage = Image.FromStream(objStream);
                        objImage.Save(writePath);
                        this.ShowMessage("保存成功，保存路径：【" + writePath + "】");
                        Thread.Sleep(200);
                    }
                }
            },
            (objException) =>
            {
                this.LogService.WriteLog("下载图片地址【" + objImageInfo.ImageUrl + "】发生异常，图片来源地址【" + objImageInfo.SourceInfo.SourceUrl + "】", "【异常消息】：" + objException.Message + "\r\n【异常内容】：" + objException.CreateMessage());
                this.ShowMessage("下载图片地址【" + objImageInfo.ImageUrl + "】发生异常，图片来源地址【" + objImageInfo.SourceInfo.SourceUrl + "】\r\n【异常消息】：" + objException.Message + "\r\n【异常内容】：" + objException.CreateMessage());
            });
        }


        protected void RepeatExecute(JiaLiContext objJiaLiContext, Action processHandle, Action<Exception> exceptionHandle, int threadSleep = 500)
        {
            if (processHandle == null)
            {
                return;
            }
            for (int i = 0; i < objJiaLiContext.ErrorRepeat; i++)
            {
                if (objJiaLiContext.IsStop)
                {
                    break;
                }
                try
                {
                    processHandle();
                    break;
                }
                catch (Exception objException)
                {
                    if (i == 4)
                    {
                        if (exceptionHandle != null)
                        {
                            exceptionHandle(objException);
                        }
                        Thread.Sleep(threadSleep);
                    }
                }
            }
        }

        public virtual ProcessResult StartCatchImages(JiaLiContext objJiaLiContext)
        {
            if (objJiaLiContext == null)
            {
                throw new ArgumentNullException("objJiaLiContext");
            }
            if (!objJiaLiContext.IsBatch)
            {
                if (objJiaLiContext.SingleUrl.IsNull())
                {
                    this.ShowMessage("单页面下载地址为空");
                    return Void();
                }
                this.SaveImagesFromCatch(objJiaLiContext, objJiaLiContext.SingleUrl);
                return Void();
            }


            TargetInfo objTargetInfo = null;
            for (int page = objJiaLiContext.PageIndex; page < objJiaLiContext.PageCount + 1; page++)
            {
                if (objJiaLiContext.IsStop)
                {
                    break;
                }
                string targetUrl = page == 1 ? objJiaLiContext.TargetUrl : objJiaLiContext.PageUrlFormat.Replace("{page}", page.ToString());
                this.ShowMessage(string.Format("---------开始把第{0}页数据加入下载队列,请求地址：【{1}】---------", page, targetUrl));
                this.RepeatExecute(objJiaLiContext,
                () =>
                {
                    objTargetInfo = this.GetTargetInfo(objJiaLiContext, targetUrl);
                    if (objTargetInfo == null || objTargetInfo.CatchUrls.IsNull())
                    {
                        this.ShowMessage("请求地址【" + targetUrl + "】没有捕获到可以下载的图片源地址");
                        return;
                    }
                    foreach (string catchUrl in objTargetInfo.CatchUrls)
                    {
                        //每次抓取资源页面执行验证
                        InvokeResult objInvokeResult = this.CheckBeforeCatch(objJiaLiContext);
                        if (objInvokeResult.ResultCode != "Success")
                        {
                            this.ShowMessage(objInvokeResult.ResultMessage);
                            return;
                        }

                        this.RepeatExecute(objJiaLiContext,
                        () =>
                        {
                            this.ShowMessage(("请求地址：【" + catchUrl + "】"));
                            this.SaveImagesFromCatch(objJiaLiContext, catchUrl);
                        },
                        (objException) =>
                        {
                            this.LogService.WriteLog("请求地址：【" + catchUrl + "】发生异常", "【异常消息】：" + objException.Message + "\r\n【异常内容】：" + objException.CreateMessage());
                            this.ShowMessage(("请求地址：【" + catchUrl + "】发生异常\r\n【异常消息】：" + objException.Message + "\r\n【异常内容】：" + objException.CreateMessage()));
                        });
                    }
                },
                (objException) =>
                {
                    this.LogService.WriteLog("请求地址：【" + targetUrl + "】发生异常", "【异常消息】：" + objException.Message + "\r\n【异常内容】：" + objException.CreateMessage());
                    this.ShowMessage(("请求地址：【" + targetUrl + "】发生异常\r\n【异常消息】：" + objException.Message + "\r\n【异常内容】：" + objException.CreateMessage()));
                });
                if (!objJiaLiContext.IsStop)
                {
                    if (this.PgaeIndexChange != null)
                    {
                        this.PgaeIndexChange(objJiaLiContext);
                    }
                    this.ShowMessage(string.Format("---------第{0}页数据下载完成---------", page));
                }
            }

            return Void();
        }

        protected virtual InvokeResult CheckBeforeCatch(JiaLiContext objJiaLiContext)
        {
            return new InvokeResult();
        }

        public override void OnException(ExceptionFilterContext filterContext)
        {
            this.ShowMessage("对不起，程序发生异常：" + filterContext.Exception.Message);
            this.LogService.WriteLog(filterContext.Exception);
            filterContext.ExceptionHandled = true;
        }
    }
}
